package com.example.demo.service.impl;

import com.example.demo.service.AccountService;
import com.example.demo.service.BlockchainService;
import com.example.demo.account.AccountInfo;
import lombok.extern.slf4j.Slf4j;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.model.TransactionReceipt;
import org.fisco.bcos.sdk.transaction.manager.AssembleTransactionProcessor;
import org.fisco.bcos.sdk.transaction.manager.TransactionProcessorFactory;
import org.fisco.bcos.sdk.transaction.model.dto.CallResponse;
import org.fisco.bcos.sdk.transaction.model.dto.TransactionResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 区块链服务实现类，处理与区块链交互的所有细节
 */
@Slf4j
@Service
public class BlockchainServiceImpl implements BlockchainService {
    
    @Resource
    private Client client;
    
    @Autowired
    private AccountService accountService;
    
    // 从配置文件注入合约地址
    @Value("0x00d6a792ae9ec2e9dbc29e06d8bf89ca15274d84")
    private String contractAddress;
    
    // 从配置文件注入合约名称
    @Value("${fisco.bcos.contract.name}")
    private String contractName;
    
    // ABI文件路径
    @Value("${fisco.bcos.contract.abiPath}")
    private String abiPath;
    
    // 资源路径
    private String abiDirPath;
    
    // 交易处理器
    private AssembleTransactionProcessor defaultTransactionProcessor;
    
    // 重试相关常量
    private static final int MAX_RETRIES = 3;
    private static final long RETRY_DELAY_MS = 2000;
    
    private TransactionReceiptListener receiptListener;
    
    /**
     * 可重试操作接口
     */
    @FunctionalInterface
    private interface RetryableOperation<T> {
        T execute() throws Exception;
    }
    
    @PostConstruct
    public void init() {
        log.info("初始化区块链服务实现类...");
        try {
            // 初始化资源路径
            this.abiDirPath = abiPath;
            
            // 初始化默认交易处理器
            defaultTransactionProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(
                    client,
                    client.getCryptoSuite().getCryptoKeyPair()
            );
            
            log.info("区块链服务初始化完成，合约地址: {}", contractAddress);
        } catch (Exception e) {
            log.error("区块链服务初始化失败: {}", e.getMessage(), e);
            throw new RuntimeException("区块链服务初始化失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 执行可重试操作
     * @param operation 可重试操作
     * @param <T> 返回类型
     * @return 操作结果
     * @throws Exception 操作异常
     */
    private <T> T executeWithRetry(RetryableOperation<T> operation) throws Exception {
        int retries = 0;
        Exception lastException = null;
        
        while (retries < MAX_RETRIES) {
            try {
                return operation.execute();
            } catch (Exception e) {
                lastException = e;
                log.warn("操作失败，将重试 ({}/{}): {}", retries + 1, MAX_RETRIES, e.getMessage());
                retries++;
                
                if (retries < MAX_RETRIES) {
                    // 等待一段时间后重试
                    try {
                        TimeUnit.MILLISECONDS.sleep(RETRY_DELAY_MS);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("重试等待被中断", ie);
                    }
                }
            }
        }
        
        // 所有重试都失败
        log.error("操作在 {} 次重试后仍然失败", MAX_RETRIES);
        throw lastException;
    }
    
    @Override
    public void addTransactionReceiptListener(TransactionReceiptListener listener) {
        this.receiptListener = listener;
    }
    
    @Override
    public void removeTransactionReceiptListener() {
        this.receiptListener = null;
    }
    
    @Override
    public void triggerReceiptListener(TransactionReceipt receipt) {
        if (receiptListener != null) {
            receiptListener.onReceipt(receipt);
        }
    }
    
    @Override
    public TransactionResponse sendTransaction(String contractMethod, List<Object> params, String userAddress) {
        log.info("发送合约交易: 方法={}, 参数={}, 发送方={}", contractMethod, params, userAddress);
        
        try {
            // 获取用户密钥对
            CryptoKeyPair userKeyPair = getUserKeyPair(userAddress);
            if (userKeyPair == null) {
                throw new RuntimeException("无法获取用户密钥对: " + userAddress);
            }
            
            // 执行合约调用
            return executeWithRetry(new RetryableOperation<TransactionResponse>() {
                @Override
                public TransactionResponse execute() throws Exception {
                    try {
                        // 加载ABI文件内容
                        String abiContent = loadAbiContent();
                        log.info("ABI文件内容长度: {}", abiContent.length());
                        
                        // 创建用户专用交易处理器
                        AssembleTransactionProcessor userTransactionProcessor = 
                            TransactionProcessorFactory.createAssembleTransactionProcessor(
                                client, userKeyPair);
                        
                        // 使用用户密钥对发送交易
                        log.info("交易实际发送方: {}", userKeyPair.getAddress());
                        
                        return userTransactionProcessor.sendTransactionAndGetResponse(
                            contractAddress,
                            abiContent,
                            contractMethod,
                            params
                        );
                    } catch (Exception e) {
                        log.error("合约调用异常: {}", e.getMessage(), e);
                        throw e;
                    }
                }
            });
        } catch (Exception e) {
            log.error("发送交易异常: {}", e.getMessage(), e);
            throw new RuntimeException("发送交易失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public CallResponse sendCall(String contractMethod, List<Object> params, String userAddress) {
        log.info("发送合约查询: 方法={}, 参数={}, 发送方={}", contractMethod, params, userAddress);
        
        try {
            // 获取用户密钥对
            CryptoKeyPair userKeyPair;
            if (userAddress != null && !userAddress.isEmpty()) {
                userKeyPair = getUserKeyPair(userAddress);
                if (userKeyPair == null) {
                    log.warn("无法获取用户密钥对，将使用默认账户: {}", userAddress);
                    userKeyPair = client.getCryptoSuite().getCryptoKeyPair();
                }
            } else {
                userKeyPair = client.getCryptoSuite().getCryptoKeyPair();
            }
            
            // 加载ABI文件内容
            final String abiContent = loadAbiContent();
            
            // 拷贝一份最终要使用的密钥对引用，以便在lambda中使用
            final CryptoKeyPair finalKeyPair = userKeyPair;
            
            // 为用户创建交易处理器
            AssembleTransactionProcessor userProcessor = 
                TransactionProcessorFactory.createAssembleTransactionProcessor(
                    client, finalKeyPair);
                    
            log.info("查询实际发送方: {}", finalKeyPair.getAddress());
            
            // 执行合约调用
            return executeWithRetry(new RetryableOperation<CallResponse>() {
                @Override
                public CallResponse execute() throws Exception {
                    try {
                        return userProcessor.sendCall(
                            finalKeyPair.getAddress(),
                            contractAddress, 
                            abiContent, 
                            contractMethod, 
                            params
                        );
                    } catch (Exception e) {
                        log.error("合约查询异常: {}", e.getMessage(), e);
                        throw e;
                    }
                }
            });
        } catch (Exception e) {
            log.error("发送查询异常: {}", e.getMessage(), e);
            throw new RuntimeException("发送查询失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public BigInteger parseReturnBigInteger(TransactionResponse response) {
        if (response == null || response.getReturnObject() == null) {
            log.warn("交易响应为空，无法解析返回值");
            return null;
        }
        
        try {
            // 处理返回值，直接去除所有方括号
            String returnStr = response.getReturnObject().toString();
            log.info("原始返回值字符串: {}", returnStr);
            
            String cleanReturnStr = returnStr.replaceAll("\\[|\\]", "").trim();
            log.info("解析后的返回值: {}", cleanReturnStr);
            
            // 转换为BigInteger
            return new BigInteger(cleanReturnStr);
        } catch (Exception e) {
            log.error("解析返回值异常: {}", e.getMessage(), e);
            return null;
        }
    }
    
    @Override
    public List<BigInteger> parseReturnBigIntegerArray(CallResponse response) {
        if (response == null) {
            log.warn("调用响应为空，无法解析返回值");
            return new ArrayList<>();
        }
        
        try {
            String callOutput = response.getValues();
            log.info("合约返回原始数据: {}", callOutput);
            
            if (callOutput == null || callOutput.isEmpty()) {
                return new ArrayList<>();
            }
            
            List<BigInteger> result = new ArrayList<>();
            
            // 去除方括号
            String cleanOutput = callOutput.replaceAll("\\[|\\]", "").trim();
            if (!cleanOutput.isEmpty()) {
                String[] idParts = cleanOutput.split(",");
                for (String idPart : idParts) {
                    try {
                        BigInteger id = new BigInteger(idPart.trim());
                        result.add(id);
                    } catch (NumberFormatException e) {
                        log.warn("无法解析ID: {}", idPart);
                    }
                }
            }
            
            return result;
        } catch (Exception e) {
            log.error("解析返回数组异常: {}", e.getMessage(), e);
            return new ArrayList<>();
        }
    }
    
    @Override
    public String[] parseCopyrightInfo(String callOutput) {
        if (callOutput == null || callOutput.isEmpty()) {
            log.warn("调用输出为空，无法解析版权信息");
            return new String[0];
        }
        
        try {
            // 清理字符串并拆分
            String cleanOutput = callOutput.trim();
            if (cleanOutput.startsWith("[") && cleanOutput.endsWith("]")) {
                cleanOutput = cleanOutput.substring(1, cleanOutput.length() - 1);
            }
            String[] parts = cleanOutput.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
            
            // 清理每个部分，移除引号
            for (int i = 0; i < parts.length; i++) {
                parts[i] = parts[i].replaceAll("\"", "").trim();
            }
            
            return parts;
        } catch (Exception e) {
            log.error("解析版权信息异常: {}", e.getMessage(), e);
            return new String[0];
        }
    }
    
    @Override
    public CryptoKeyPair getUserKeyPair(String userAddress) {
        try {
            if (userAddress == null || userAddress.isEmpty()) {
                log.warn("用户地址为空，将使用默认账户");
                return client.getCryptoSuite().getCryptoKeyPair();
            }
            
            // 获取账户信息
            AccountInfo accountInfo = accountService.getAccount(userAddress);
            if (accountInfo == null) {
                log.warn("账户不存在: {}", userAddress);
                return null;
            }
            
            // 获取用户私钥
            String privateKey = accountInfo.getPrivateKey();
            if (privateKey == null || privateKey.isEmpty()) {
                log.warn("用户私钥未设置: {}", userAddress);
                return null;
            }
            
            // 使用私钥创建密钥对
            try {
                // 如果私钥以0x开头，需要去除
                if (privateKey.startsWith("0x")) {
                    privateKey = privateKey.substring(2);
                }
                
                // 创建密钥对
                CryptoKeyPair keyPair = client.getCryptoSuite().createKeyPair(privateKey);
                
                // 验证地址是否匹配
                if (!keyPair.getAddress().equalsIgnoreCase(userAddress)) {
                    log.error("生成的地址与用户地址不匹配: 生成={}, 用户={}", 
                        keyPair.getAddress(), userAddress);
                    return null;
                }
                
                log.info("成功创建用户密钥对: {}", userAddress);
                return keyPair;
            } catch (Exception e) {
                log.error("创建密钥对失败: {}", e.getMessage(), e);
                return null;
            }
        } catch (Exception e) {
            log.error("获取用户密钥对异常: {}", e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 加载ABI文件内容
     * @return ABI文件内容
     * @throws Exception 加载异常
     */
    private String loadAbiContent() throws Exception {
        String abiFile = abiDirPath + contractName + ".abi";
        log.info("加载ABI文件: {}", abiFile);
        
        File file = new File(abiFile);
        if (!file.exists()) {
            throw new RuntimeException("ABI文件不存在: " + abiFile);
        }
        
        return new String(java.nio.file.Files.readAllBytes(file.toPath()));
    }
} 